﻿// License: The MIT License (MIT) Copyright (c) 2011..2012 Barend Gehrels

// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:

// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media.Imaging;
using System.Windows.Threading;

namespace GrooveSharp
{

    public partial class GrooveSharpMainWindow : Window
    {
        private KeyboardWatcher m_KeyboardWatcher;


        private DispatcherTimer m_ActivityTimer;
        private int m_ActivityCounter = 0;

        private SizeManager m_SizeManager = new SizeManager();
        private IList<FolderWatcher> m_FolderWatchers = new List<FolderWatcher>();

        private UpdatePlayDates m_updatePlayDates = new UpdatePlayDates();

        private IList<IPlayer> m_players = new List<IPlayer>();

        public GrooveSharpMainWindow()
        {
            InitializeComponent();
        }

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            if (DesignerProperties.GetIsInDesignMode(this))
            {
                return;
            }

            foreach (var path in Db.Connection().Select<GroovePath>("select * from paths where is_active=1"))
            {
                m_FolderWatchers.Add(new FolderWatcher(path));
            }

            m_ActivityTimer = new DispatcherTimer();
            m_ActivityTimer.Interval = TimeSpan.FromSeconds(1.0);
            m_ActivityTimer.Tick += new EventHandler(activityTimer_Tick);
            m_ActivityTimer.Start();

            imageMoveGrip.Visibility = Visibility.Hidden;

#if DEBUG
            buttonTest.Visibility = Visibility.Visible;
#endif

            theCollection.Scan();

            m_KeyboardWatcher = new KeyboardWatcher
            (
                new Key[] 
                {
                    Key.MediaPlayPause,
                    Key.MediaNextTrack,
                    Key.MediaPreviousTrack
                } 
            );
            m_KeyboardWatcher.KeyDown += new KeyboardWatcher.KeyboardWatcherEventHandler(KeyboardWatcher_KeyDown);

            m_players.Add(theMainPlayer);
            m_players.Add(theNormalInfo);
            m_players.Add(theCompactInfo);
            m_players.Add(theCurrentPlayList);
            m_players.Add(m_updatePlayDates);
            m_players.Add(new Announcer());
        }

        private void Window_Closed(object sender, EventArgs e)
        {
            theConfigurationEditor.CommitIfNecessary();

            // Stop if necessary, at least store end-date
            m_updatePlayDates.Stop();
        }

        private void Play(TrackInfo info)
        {
            try
            {
                // First check to update later.
                bool wasPlaying = theMainPlayer.Playing();

                // Now update stops - where necessary
                if (wasPlaying)
                {
                    theMainPlayer.Stop();
                    m_updatePlayDates.Stop();
                }

                if (info != null)
                {
                    foreach (IPlayer player in m_players)
                    {
                        player.Play(info);
                    }
                }

                UpdatePauseButton();
            }
            catch(Exception e)
            {
                App.ReportException(e);
            }
        }

        private void UpdatePauseButton()
        {
            BitmapImage image = new BitmapImage();
            image.BeginInit();
            image.UriSource = new Uri(String.Format("pack://application:,,/Images/{0}.gif", theMainPlayer.IsPaused() ? "play" : "pause"));
            image.EndInit();
            imagePlayPause.Source = image;
        }

        private void TogglePause()
        {
            theMainPlayer.TogglePause();
            UpdatePauseButton();
        }
        
        private void Next()
        {
            Play(TrackInfoCollection.Next());
        }

        private void Previous()
        {
            Play(TrackInfoCollection.Previous());
        }

        private void NextIfNotStarted()
        {
            if (! theMainPlayer.Playing())
            {
                Next();
            }
        }

        private void AddTrack(TrackInfo track)
        {
            TrackInfoCollection.Add(track);
        }

        private void AddTrack(string fileName)
        {
            AddTrack(TrackInfoHelper.GetTrackInfo(-1, fileName));
        }

        private void MusicCollection_AddTrack(object sender, RoutedEventArgs e)
        {
            foreach (TrackInfo track in theCollection.SelectedTracks())
            {
                AddTrack(track);
            }
            NextIfNotStarted();
        }

        private void buttonNext_Click(object sender, RoutedEventArgs e)
        {
            Next();
        }

        private void mainTabContro1_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e)
        {
            if (e.AddedItems.Contains(tabItemCollection))
            {
                theCollection.LoadCollectionIfEmpty();
            }

            if (e.RemovedItems.Contains(tabItemSettings))
            {
                if (theConfigurationEditor.CommitIfNecessary())
                {
                    theCollection.Refresh();
                    theCollection.Scan();
                }
            }
            else if (e.AddedItems.Contains(tabItemSettings))
            {
                theConfigurationEditor.Refresh();
            }

        }

        private void buttonPrev_Click(object sender, RoutedEventArgs e)
        {
            Previous();
        }

        private void buttonPause_Click(object sender, RoutedEventArgs e)
        {
            TogglePause();
        }

        private void KeyAction(Key key)
        {
            switch (key)
            {
                case System.Windows.Input.Key.MediaPlayPause : 
                    TogglePause(); 
                    break;
                case System.Windows.Input.Key.MediaPreviousTrack :
                    Previous(); 
                    break;
                case System.Windows.Input.Key.MediaNextTrack :
                    Next(); 
                    break;
            }
        }


        private void KeyboardWatcher_KeyDown(object sender, KeyboardWatcher.KeyboardEventArgs e)
        {
            KeyAction(e.Key);
            AnyInteraction();
        }

        private void theMainPlayer_MusicStart(object sender, RoutedEventArgs e)
        {
            UpdatePauseButton();
        }

        private void theMainPlayer_MusicFade(object sender, RoutedEventArgs e)
        {
            theNormalInfo.FadeOut();
        }

        private void theMainPlayer_MusicEnd(object sender, RoutedEventArgs e)
        {
            foreach (IPlayer player in m_players)
            {
                player.Stop();
            }
            Next();
        }

        private void buttonExit_Click(object sender, RoutedEventArgs e)
        {
            Application.Current.Shutdown();
        }

        // Users can drop MP3 or M4A files on any part of the window as well
        private void Window_Drop(object sender, DragEventArgs e)
        {
            if (e != null && e.Data != null)
            {
                object filenames = e.Data.GetData(DataFormats.FileDrop, true);
                if (filenames is IEnumerable<string>)
                {
                    foreach (string f in filenames as IEnumerable<string>)
                    {
                        string fu = f.ToUpper();
                        if (fu.EndsWith(".MP3") || fu.EndsWith(".M4A"))
                        {
                            AddTrack(f);
                        }
                    }
                    e.Handled = true;
                    NextIfNotStarted();
                }
            }
        }

        // Rating
        private void SaveAndSynchronizeRating(TrackRating rating)
        {
            if (rating.TrackId > 0)
            {
                Db.Connection().Insert(rating);
                TrackInfoCollection.SetRating(rating);
            }
        }

        private void SaveAndSynchronizeRating(Rating rating)
        {
            SaveAndSynchronizeRating(new TrackRating() { TrackId = TrackInfoCollection.Id(), Value = rating.RatingValue });
        }
        
        private void theCompactInfo_RatingChanged(object sender, RoutedEventArgs e)
        {
            SaveAndSynchronizeRating(theCompactInfo.theRating);
        }

        private void theNormalInfo_RatingChanged(object sender, RoutedEventArgs e)
        {
            SaveAndSynchronizeRating(theNormalInfo.theRating);
        }

        private void theCurrentPlayList_RatingChanged(object sender, RoutedEventArgs e)
        {
            SaveAndSynchronizeRating(theCurrentPlayList.LastChangedRating);
        }

        private void theCurrentPlayList_PlayOtherTrack(object sender, RoutedEventArgs e)
        {
            Play(TrackInfoCollection.SetPlayingId(theCurrentPlayList.SelectedId));
        }

        #region HIDING BUTTONS IF NO INTERACTION
        private void setVisibilityOfMany(Visibility setting)
        {
            topStack.Visibility = setting;
            tabItemPlayer.Visibility = setting;
            tabItemCollection.Visibility = setting;
            tabItemCurrentPlayList.Visibility = setting;
            tabItemSettings.Visibility = setting;
            tabItemHelp.Visibility = setting;

            // Setting bottomStack triggers a mouse-change event (curiously enough)
            //bottomStack.Visibility = setting;
            buttonPrev.Visibility = setting;
            buttonNext.Visibility = setting;
            buttonPause.Visibility = setting;

            if (this.WindowState != WindowState.Maximized)
            {
                imageMoveGrip.Visibility = setting;
            }

#if DEBUG
            buttonTest.Visibility = setting;
#endif
        }

        private void activityTimer_Tick(object sender, EventArgs e)
        {
            if (mainTabControl.SelectedIndex == 0 || m_SizeManager.IsCompact)
            {
                m_ActivityCounter++;
            }

            // At 5 seconds:
            if (m_ActivityCounter == 5)
            {
                // Collapsed does not work because triggers a mouse-change event (curiously enough)
                setVisibilityOfMany(Visibility.Hidden);
                App.Current.MainWindow.Cursor = Cursors.None;

                // DON'T uncomment this: it restarts the MP3/M4A. Alas, probably bug in WPF.
                /*if (isCompact)
                {
                    this.ResizeMode = ResizeMode.NoResize;
                }*/

            }
        }

        private void AnyInteraction()
        {
            // Reset the timer and make buttons visible
            m_ActivityCounter = 0;
            setVisibilityOfMany(Visibility.Visible);
            App.Current.MainWindow.Cursor = Cursors.Arrow;
            this.ResizeMode = ResizeMode.CanResizeWithGrip;
        }

        private void Window_PreviewTextInput(object sender, TextCompositionEventArgs e)
        {
            // This event fires on a normal key, but not on shift/ctrl/etc
            // which is handled in PreviewKeyDown
            AnyInteraction();
        }

        private void Window_PreviewMouseMove(object sender, System.Windows.Input.MouseEventArgs e)
        {
            AnyInteraction();
        }

        #endregion

        #region RESIZING
        private void buttonState_Click(object sender, RoutedEventArgs e)
        {
            bool setMaximized = this.WindowState != WindowState.Maximized;
            this.WindowState = setMaximized ? WindowState.Maximized : WindowState.Normal;
            //imageMoveGrip.Visibility = setMaximized ? Visibility.Hidden : Visibility.Visible;
            //this.Topmost = !setMaximized;
        }

        private void SetButtonForCompact(Button b, Image i, bool compact)
        {
            b.Width = compact ? 16 : 40;
            b.Height = compact ? 16 : 40;
            i.Width = compact ? 8 : 18;
            i.Height = compact ? 8 : 18;

            b.Margin = new Thickness(compact ? 3 : 10);
        }

        private void SetCompactness(bool toCompact)
        {
            if (toCompact)
            {
                mainTabControl.Visibility = Visibility.Collapsed;
                theCompactInfo.Visibility = Visibility.Visible;
                theMainPlayer.mediaProgressBar.Height = 5;
                panelHeader.Margin = new Thickness(0, 2, 2, 0);
            }
            else
            {
                mainTabControl.Visibility = Visibility.Visible;
                theCompactInfo.Visibility = Visibility.Collapsed;
                theMainPlayer.mediaProgressBar.Height = 15;
                panelHeader.Margin = new Thickness(0, 8, 8, 0);

            }
            SetButtonForCompact(buttonPrev, imagePrev, toCompact);
            SetButtonForCompact(buttonNext, imageNext, toCompact);
            SetButtonForCompact(buttonPause, imagePlayPause, toCompact);
            SetButtonForCompact(buttonTest, imagePlayPause, toCompact);
        }

        private void buttonCompact_Click(object sender, RoutedEventArgs e)
        {
            m_SizeManager.ToggleCompactness(this);
            SetCompactness(m_SizeManager.IsCompact);
        }

        private void Window_StateChanged(object sender, EventArgs e)
        {
            System.Diagnostics.Debug.Print("State changed");
            bool nowMaximized = this.WindowState == WindowState.Maximized;
            imageMoveGrip.Visibility = nowMaximized ? Visibility.Hidden : Visibility.Visible;
            this.Topmost = !nowMaximized;

            SetCompactness(m_SizeManager.IsCompact && !nowMaximized);
        }

        private void Window_LocationChanged(object sender, EventArgs e)
        {
            m_SizeManager.SizeOrLocationChanged(this);
        }

        private void Window_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            m_SizeManager.SizeOrLocationChanged(this);
        }

        #endregion

        #region DRAG MOVE USING GRIP

        // http://social.msdn.microsoft.com/Forums/en/wpf/thread/f9ff4c24-506b-4683-b023-34a73f48feee
        private bool m_Moving = false;

        private void moveGripImage_MouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
        {
            m_Moving = true;
        }

        private void moveGripImage_MouseLeftButtonUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
        {
            // We set m_moving to false but this event is indeed not fired at all:
            // http://stackoverflow.com/questions/1917176/c-sharp-wpf-dragmove-and-click
            // Anyway, it is working correctly because of MouseLeave
            m_Moving = false;
        }

        private void moveGripImage_MouseLeave(object sender, MouseEventArgs e)
        {
            m_Moving = false;
        }

        private void moveGripImage_MouseMove(object sender, MouseEventArgs e)
        {
            if (m_Moving)
            {
                this.DragMove();
            }

        }

        #endregion

        #region TEST CODE ONLY
        // ------------------------------------------------------------------------------------
        //private void AddFolderToQueue(string folderName)
        //{
        //    foreach (string file in Directory.GetFiles(folderName, "*.mp3"))
        //    {
        //        AddTrack(new TrackInfo() { FileName = file });
        //    }
        //}

        private void buttonTest_Click(object sender, RoutedEventArgs e)
        {
            // Short tracks
            if (TrackInfoCollection.Empty())
            {
                AddTrack(@"c:\users\Barend\Music\Prince\Gold Experience\06 NPG Operator.mp3");
                AddTrack(@"c:\users\Barend\Music\David Bowie\Outside [Australia]\17 Segue- Nathan Adler.mp3");
                AddTrack(@"c:\users\Barend\Music\Pink Floyd\The Wall Disc 2\11 Stop.mp3");

                // Albums
                //addFolderToQueue(@"c:\Users\Barend\Music\Radiohead\OK Computer");
                //addFolderToQueue(@"c:\Users\Barend\Music\Queen\Innuendo");
                NextIfNotStarted();
            }

            //mainDockPanel.Visibility = Visibility.Collapsed;
            //compactPlayer.Visibility = Visibility.Visible;

            //theCompactInfo.labelAlbum.Text = "test";
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            //mainDockPanel.Visibility = Visibility.Visible;
            //compactPlayer.Visibility = Visibility.Collapsed;
        }

        #endregion



        public void SetPowerMode(Microsoft.Win32.PowerModes mode)
        {
            theMainPlayer.SetPowerMode(mode);
        }
    }
}
